package tcln.sors.model.core.util.dataloader;

import org.springframework.context.ApplicationContext;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.TransactionDefinition;
import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.support.DefaultTransactionDefinition;

import tcln.commons.assertion.Assert;
import tcln.commons.collections.ArrayUtils;
import tcln.commons.command.Command;
import tcln.commons.command.CommandExecutor;
import tcln.commons.exception.BaseRuntimeException;
import tcln.commons.reflection.ClassUtils;
import tcln.sors.model.contact.dom.Contact;
import tcln.sors.model.core.dao.DaoFactory;
import ch.elca.el4j.core.context.ModuleApplicationContext;
import ch.elca.el4j.core.context.ModuleApplicationContextConfiguration;

/**
 * @author Truong Chau Lien Ngoc (lienngoc@gmail.com)
 */
public class DataLoader extends CommandExecutor<Void> {
    private ApplicationContext applicationContext;
    private DaoFactory daoRegistry;

    /**
     * Launches the data loader.
     * 
     * @param args
     */
    public static void main(String[] args) {
        try {
            DataLoader dataLoader = new DataLoader();

            dataLoader.getLogger().info("Data loader is launching ...");
            dataLoader.initApplicationContext();
            dataLoader.getLogger().info("Application context has been initialized.");

            if (ArrayUtils.isEmpty(args)) {
                dataLoader.getLogger().info("No command to execute.");
            } else {

                dataLoader.getLogger().info("Registering inner commands ...");
                for (int i = 0; i < args.length; i++) {
                    String commandClass = args[i];

                    Class<?> commandCls = ClassUtils.loadClass(commandClass, DataLoader.class);
                    Command<Object, Object> command = (Command) commandCls.newInstance();

                    dataLoader.add(command);
                    dataLoader.getLogger().info(" > Command '{0}' has been registered", command.getName());
                }
                
                dataLoader.getLogger().info("Executing data loader ...");
                dataLoader.execute(null);
            }

            dataLoader.getLogger().info("DataLoader stops successfully.");

        } catch (Throwable t) {
            t.printStackTrace();
            System.exit(1);
        }
    }

    private void initApplicationContext() {
        ModuleApplicationContextConfiguration conf = new ModuleApplicationContextConfiguration();
        conf.setAllowBeanDefinitionOverriding(true);
        conf.setInclusiveConfigLocations(new String[] { "classpath:/core-config.xml",
                "classpath:/datasource-config.xml", "classpath:/service-config.xml", "classpath:/dao-config.xml",
                "classpath:/scenarios/dataaccess/hibernate/hibernateDefaultBeans.xml" });

        applicationContext = new ModuleApplicationContext(conf);
        daoRegistry = applicationContext.getBean("daoFactory", DaoFactory.class);
    }

    /**
     * {@inheritDoc}
     */
    @Override
    protected Object executeCommand(Command<Object, Object> command, Object input, Object... additionalParams) {
        Object result = null;
        TransactionStatus transactionStatus = getTransaction();

        try {
            logger.info(" > Executing command '{0}' ...", command.getName());
            result = super.executeCommand(command, input, additionalParams);
            daoRegistry.getDaoFor(Contact.class).flushSession();
            commitTransaction(transactionStatus);
            logger.info("   > Done.");

        } catch (Throwable t) {
            logger.error(t, "   > Error.");
            rollbackTransaction(transactionStatus);
            BaseRuntimeException.wrapIfNecessary(t);
        }

        return result;
    }
    
    protected final TransactionStatus getTransaction() {
        PlatformTransactionManager txManager = applicationContext.getBean("transactionManager",
                PlatformTransactionManager.class);
        TransactionStatus txStatus = txManager.getTransaction(new DefaultTransactionDefinition(
                TransactionDefinition.PROPAGATION_REQUIRED));
        return txStatus;
    }

    protected final void commitTransaction(final TransactionStatus txStatus) {
        if (!txStatus.isCompleted()) {
            PlatformTransactionManager txManager = applicationContext.getBean("transactionManager",
                    PlatformTransactionManager.class);
            txManager.commit(txStatus);
        }
    }

    protected final void rollbackTransaction(final TransactionStatus txStatus) {
        if (!txStatus.isCompleted()) {
            PlatformTransactionManager txManager = applicationContext.getBean("transactionManager",
                    PlatformTransactionManager.class);
            txManager.rollback(txStatus);
        }
    }

    /**
     * {@inheritDoc}
     */
    @Override
    protected void ensurePreconditions(Void input, Object... additionalParams) {
        super.ensurePreconditions(input, additionalParams);

        Assert.STATE.notNull(applicationContext, "[applicationContext] must not be null");
        Assert.STATE.notNull(daoRegistry, "[daoRegistry] must not be null");

        for (Command<?, ?> c : getChildCommands()) {
            DataLoadingCommand command = (DataLoadingCommand) c;
            command.setApplicationContext(applicationContext);
            command.setDaoFactory(daoRegistry);
        }
    }
}
